#include "NativePipelineElements.h"

#include <QFile>
#include <QTextStream>

typedef struct {
    unsigned short type;
    unsigned short thread;
    unsigned long timestamp;
    unsigned long arg1;
    unsigned long arg2;
} InstrumentRecord;

inline unsigned short swapU16(unsigned short value)
{
    return ((value >> 8) | (value << 8));
}

inline unsigned long swapU32(unsigned long value)
{
    return ((swapU16(value) << 16) | swapU16(value >> 16));
}

// ECInstrumentationBufferReader

ECInstrumentationBufferReader::ECInstrumentationBufferReader()
        : PipelineElement(), swapBytes(false)
{
}

void ECInstrumentationBufferReader::convertInstrumentationBufferFast()
{
    QFile file;
    file.setFileName(bufferPath);
    file.open(QIODevice::ReadOnly);
    QByteArray byteArray = file.readAll();
    
    int rows = byteArray.size() / sizeof(InstrumentRecord);
    InstrumentRecord *events = (InstrumentRecord *)byteArray.data();
    
    if (swapBytes) {
        for (int row = 0; row < rows; row++) {
            // swap all short values in the struct
            events[row].type = swapU16(events[row].type);
            events[row].thread = swapU16(events[row].thread);
            
            // swap all long vaues in the struct
            events[row].timestamp = swapU32(events[row].timestamp);
            events[row].arg1 = swapU32(events[row].arg1);
            events[row].arg2 = swapU32(events[row].arg2);
        }
    }
    
    QTextStream stream(&buffer);
    stream << "<events>";
    for (int row = 0; row < rows; row++) {
        QString eventNode = QObject::tr(
                "<event>"
                "<index>%1</index>"
                "<type>0x%2</type>"
                "<thread>%3</thread>"
                "<timestamp>%4</timestamp>"
                "<arg1>0x%5</arg1>"
                "<arg2>0x%6</arg2>"
                "</event>")
                .arg(row)
                .arg(events[row].type, 4, 16, QLatin1Char('0'))
                .arg(events[row].thread)
                .arg(events[row].timestamp)
                .arg(events[row].arg1, 8, 16, QLatin1Char('0'))
                .arg(events[row].arg2, 8, 16, QLatin1Char('0'));
        stream << eventNode;
    }
    stream << "</events>";
}

void ECInstrumentationBufferReader::convertInstrumentationBuffer()
{
    QFile file;
    file.setFileName(bufferPath);
    file.open(QIODevice::ReadOnly);
    QByteArray byteArray = file.readAll();
    
    int rows = byteArray.size() / sizeof(InstrumentRecord);
    InstrumentRecord *events = (InstrumentRecord *)byteArray.data();
    
    if (swapBytes) {
        for (int row = 0; row < rows; row++) {
            // swap all short values in the struct
            events[row].type = swapU16(events[row].type);
            events[row].thread = swapU16(events[row].thread);
            
            // swap all long vaues in the struct
            events[row].timestamp = swapU32(events[row].timestamp);
            events[row].arg1 = swapU32(events[row].arg1);
            events[row].arg2 = swapU32(events[row].arg2);
        }
    }
    
    QDomDocument eventsDomDocument;
    QDomElement root = eventsDomDocument.createElement("events");
    eventsDomDocument.appendChild(root);
    
    for (int row = 0; row < rows; row++) {
        QString eventIndex = QObject::tr("%1").arg(row);
        QString eventType = QObject::tr("0x%1").arg(events[row].type, 4, 16, QLatin1Char('0'));
        QString eventThread = QObject::tr("%1").arg(events[row].thread);
        QString eventTimestamp = QObject::tr("%1").arg(events[row].timestamp);
        QString eventArg1 = QObject::tr("0x%1").arg(events[row].arg1, 8, 16, QLatin1Char('0'));
        QString eventArg2 = QObject::tr("0x%1").arg(events[row].arg2, 8, 16, QLatin1Char('0'));
        
        QDomElement indexElement = eventsDomDocument.createElement("index");
        QDomElement typeElement = eventsDomDocument.createElement("type");
        QDomElement threadElement = eventsDomDocument.createElement("thread");
        QDomElement timestampElement = eventsDomDocument.createElement("timestamp");
        QDomElement arg1Element = eventsDomDocument.createElement("arg1");
        QDomElement arg2Element = eventsDomDocument.createElement("arg2");
        
        indexElement.appendChild(eventsDomDocument.createTextNode(eventIndex));        
        typeElement.appendChild(eventsDomDocument.createTextNode(eventType));
        threadElement.appendChild(eventsDomDocument.createTextNode(eventThread));
        timestampElement.appendChild(eventsDomDocument.createTextNode(eventTimestamp));
        arg1Element.appendChild(eventsDomDocument.createTextNode(eventArg1));
        arg2Element.appendChild(eventsDomDocument.createTextNode(eventArg2));
        
        QDomElement eventElement = eventsDomDocument.createElement("event");
        eventElement.appendChild(indexElement);
        eventElement.appendChild(typeElement);
        eventElement.appendChild(threadElement);
        eventElement.appendChild(timestampElement);
        eventElement.appendChild(arg1Element);
        eventElement.appendChild(arg2Element);
        root.appendChild(eventElement);
    }
    
    buffer = eventsDomDocument.toByteArray();
}

void ECInstrumentationBufferReader::setup(const QDomNode& setupDescription)
{
}

void ECInstrumentationBufferReader::process()
{
    convertInstrumentationBufferFast();
}


// BufferMuxer

BufferMuxer::BufferMuxer()
        : PipelineProcessingElement(), secondaryParents()
{
}

void BufferMuxer::setup(const QDomNode& setupDescription)
{
}

void BufferMuxer::process()
{
    buffer = parent->result();
    
    for (int index = 0; index < secondaryParents.size(); index++)
       buffer.append(secondaryParents.at(index)->result());
}
